class User < ActiveRecord::Base
  has_many :bets do
     #Friends placing bets will include a friends_count
     #friend_bets = User.first.bets.friends_scope.all :order=>"friends_count DESC" (order based on time at which friends placed bet)
     #friend_bets.first.friends_count
    def friends_scope
      Bet.scoped(
        :select=>"#{Bet.table_name}.*,count(match_id) AS friends_count",
        :group=>"match_id",
        :conditions=>["#{Bet.table_name}.user_id IN (?)",proxy_owner.all_friend_ids]
      )
    end
  end
  has_many :user_actions
  has_many :ilogs
  has_many :got_challenges,:class_name=>"Challenge",:foreign_key=>:to_user_id,:conditions=>["mode = ? and hide is null","bet_0"],:order=>'id desc'
  has_many :sent_challenges,:class_name=>"Challenge",:foreign_key=>:from_user_id,:conditions=>["mode =? and hide is null","bet_0"],:order=>'id desc'
  has_many :result_sent_ches,:class_name=>"Challenge",:foreign_key=>:from_user_id,:conditions=>["mode =? and hide is null","bet_2"],:order=>'id desc'
  has_many :result_got_ches,:class_name=>"Challenge",:foreign_key=>:to_user_id,:conditions=>["mode =? and hide is null","bet_2"],:order=>'id desc'
  has_many :point_histories
  has_many :matches,:through=>:bets,:source=>:match do 
    def next
      proxy_owner.matches.played(false).first(:order=>"begin_at asc ,id asc")
    end

    # User's friend's bets
    # Order based on friends_count 
    def friends_scope
      Match.scoped(
        :select=>"#{Match.table_name}.*,
        (
          SELECT count(*) FROM #{Bet.table_name} 
          WHERE bets.match_id = #{Match.table_name}.id 
          AND #{Bet.table_name}.user_id IN (#{proxy_owner.all_friends || 'NULL'})
        )
        AS friends_count"
      )
    end

  end
  after_create :add_init_point

  def add_init_point
    self.point = PointConfig.find_by_name("user_start").point
    self.save
    if ph = PointHistory.find(:first,:conditions=>["action='invite friend' and related_user_id=?",self.id])
      u = User.find(ph.user_id)
      u.point = u.point + PointConfig.find_by_name("invite_received").point
      u.invitation_accept_number = u.invitation_accept_number.to_i + 1
      u.save
    end
  end
  
  def other_friends(sid)
     other_ids = (self.all_friends.split(",") - joined_ids = Match.find(sid).bets.map{|b| b.user_id}).join(",") 
     User.find(:all,:conditions=>"id in (#{other_ids})",:order=>'name')
  end
  # get bet through match
  def bet(match)
    Bet.find(:first,:conditions=>["user_id = ? and match_id = ?",self.id,match.id])
  end


  # User's challenge status for a match
  # to_user: the user on the other side of the challenge
  # sid: match id
  def challenge_status(to_user,sid)
    ch = Challenge.find(:first,:conditions=>["match_id = ? and from_user_id = ? and to_user_id = ? ",sid,self.id,to_user.id])  
    ch2 = Challenge.find(:first,:conditions=>["match_id = ? and to_user_id = ? and from_user_id = ?",sid,self.id,to_user.id])  
    res = {:ch=>nil,:status=>nil}

    if ch 
      res[:ch]=ch
      if ch.mode == "bet_0"
        if ch.match.begin_at < Time.now 
          res[:status] = "ch_expired" 
		else
	  	  res[:status] = "ch_sent"
		end
      elsif ch.mode == "bet_1"
        if ch.match.begin_at < Time.now 
          res[:status] = "ch_expired" 
		else
	  	  res[:status] = "ch_accept"
	 	end
      elsif ch.mode == "bet_2"
		res[:status] = "ch_result"
      else
      end
      return res
    elsif ch2
      res[:ch]=ch2
      if ch2.mode == "bet_0"
        if ch2.match.begin_at < Time.now 
          res[:status] = "ch_expired" 
		else
	 	  res[:status] = "ch_wait_accept"
		end
      elsif ch2.mode == "bet_1"
        if ch2.match.begin_at < Time.now 
          res[:status] = "ch_expired" 
		else
		  res[:status] = "ch_was_accept"
		end
      elsif ch2.mode == "bet_2"
		res[:status] = "ch_result"
      end
    else
      if Match.find(sid).begin_at < Time.now
		res[:status] = "ch_expired"
      else
		res[:status] = "place_ch_link"
      end
    end
    return res
  end
  
  # Add WinPoints to user
  def add_point(p)
      self.point = self.point.to_i + p
      self.save
  end
  # Check if user bought free wins?
  def bought_win?
     BuyWin.find(:first,:conditions=>["user_id=? and point_config_id=0 and winstreak_id=?",self.id,self.winstreak.id])
  end
  # User bought free wins
  def bought_wins
     BuyWin.find(:all,:conditions=>["user_id=? and point_config_id>0 and winstreak_id=?",self.id,self.winstreak.id])
  end

  has_many :winstreaks do
    def next_position
      max_position_winstreak = first :order=>"position desc"
      max_position_winstreak.nil? ? 0 : max_position_winstreak.position + 1
    end
  end
  belongs_to :winstreak # current winstreak
  belongs_to :prize

  named_scope :rank,:order=>"#{Winstreak.table_name}.streak_count desc",:include=>[:winstreak]
  # Ensure winstreak exists
  def winstreak_with_ensure_exists
    winstreak_without_ensure_exists || reset_winstreak
  end

  def prize_with_ensure_selected
    prize_without_ensure_selected ||  auto_assign_prize
  end

  alias_method_chain :winstreak,:ensure_exists
  alias_method_chain :prize,:ensure_selected

  # Auto assign initial prize to user
  def auto_assign_prize
    self.prize = Prize.first
    self.save
    prize
  end

  # Check if user is an administrator
  def admin?
    [752103199,523941085,5408990,826873].include?(facebook_id)
  end

  # Best streak history (this should be cached)
  def best_winstreak
    @best_winstreak ||= winstreaks.first(:order=>"streak_count desc,id desc")
  end

  # Current winstreak history
  def current_winstreak_count
    #winstreak.nil? ? 0 : winstreak.streak_count 
    # add buy win times to current_count
    winstreak.nil? ? 0 : winstreak.streak_count
  end

  # Changing a prize
  # Maybe only when winstreak is zero？
  def change_prize(prize)
    if winstreak.nil? || winstreak.streak_count.to_i.zero?
      self.prize = prize
      reset_winstreak if winstreak.nil?
      winstreak.prize = prize
      winstreak.save
      save
    else
      false
    end
  end

  # User's friends
  def friends
    User.scoped(:conditions=>["#{User.table_name}.id in (?)",friend_ids])
  end

  # All friends ids 
  def all_friend_ids
    @all_friend_ids ||= all_friends.to_s.split(',').compact
  end

  # All friends ids 
  def friend_ids
    @friend_ids ||= Facebooker::Session.current.user.friends.collect(&:facebook_id)
  end

  # Reset winstreak count
  def reset_winstreak
    new_winstreak = winstreaks.build(:prize=>prize)
    new_winstreak.position = winstreaks.next_position
    new_winstreak.save
    self.winstreak = new_winstreak
    # should re-calculate the previous best streak here
    save(false)
    new_winstreak
  end

  # No. of wins until prize can be won
  # If no prize was selected; maybe the user didn't make any bets. If so, return nil
  # Otherwise return number of wins required for the prize
  def streak_remain
    winstreak && winstreak.streak_remain   if prize
  end

  def facebook_user
    @facebook_user ||= Facebooker::User.new id
  end

  # Calculate win/loss percentage
  def calc_percentage
    self.wins_count = bets.played(true).win(true).count + self.bought_wins.size
    self.losses_count = bets.played(true).win(false).count
    if wins_count.zero? && losses_count.zero?
      self.win_percentage = 100.0
    else
      self.win_percentage = ((wins_count/(wins_count + losses_count).to_f)*100).ceil
    end
    save
    self.win_percentage
  end

  # Create a user or find one
  def self.for(facebook_session)
    return if facebook_session.nil?
    uid = facebook_session.user.facebook_id
    user = User.find_by_facebook_id(uid)
    if user
      return user
    else 
      user = User.new
      facebook_user = facebook_session.user
        user.id = uid
        user.facebook_id = uid
        user.last_name         = facebook_session.user.last_name                                                 
        user.first_name        = facebook_session.user.first_name
        user.name			   = facebook_session.user.name
        user.affiliations      = facebook_session.user.affiliations.map{|a| a.year + ":" + a.name}.join(";") if facebook_session.user.affiliations        
        user.timezone          = facebook_session.user.timezone 
        user.birthday          = facebook_session.user.birthday                                                    
        user.sex          	   = facebook_session.user.sex         
        user.hometown_location = (facebook_session.user.hometown_location.city rescue "") + "," +  (facebook_session.user.hometown_location.state rescue "") + "," + (facebook_session.user.hometown_location.country rescue "")  if facebook_session.user.hometown_location        
        user.current_location = (facebook_session.user.current_location.city rescue "") + "," +  (facebook_session.user.current_location.state rescue "") + "," + (facebook_session.user.current_location.country rescue "")  if facebook_session.user.current_location
        user.activities        = facebook_session.user.activities        
        user.interests         = facebook_session.user.interests                                                 
        user.hs_info           = facebook_session.user.hs_info.hs1_name if facebook_session.user.hs_info
        user.education_history = facebook_session.user.education_history.map{|h| h.name}.join(";")   if facebook_session.user.education_history
        user.work_history      = facebook_session.user.work_history.map{|h| h.company_name + ":" + h.position}.join(";") if facebook_session.user.work_history        
        user.wall_count        = facebook_session.user.wall_count        
        user.which_install = 1
      user.save
      return user
    end
  end

  #this should return the next bet based on match time  
  def next_bet
      bets = self.bets.played(false)
      if bets.nil?
      	return nil
      end
      	
      
      nextBet = bets[0]
      for bet in bets
      	if bet.match.begin_at < nextBet.match.begin_at
      		nextBet = bet
      	end
      end
      
      return nextBet
  
  end

  # Current bet information
  def current_bet
    bet = self.bets.played(false).first
    #if bet and bet.match and bet.match.begin_at < Time.now
    #  return bet
    #else
    #  return nil
    #end
    #if current_streak_bets.size > 0 
    #  current_streak_bets.last
    #else
    #  return nil
    #end
  end
  
  # Current bets in current streak 
  def current_streak_bets
    bets = self.bets.played(false).all :include=>{:match=>[:home_team,:away_team]},
      :order=>"#{Match.table_name}.begin_at ASC"
    current_bets = winstreak && winstreak.bets(:include=>[:match],:order=>"#{Match.table_name}.begin_at ASC")
    current_bets ||= []
    current_bets + bets
  end

  # Current streak's bet count 
  def current_streak_bets_count
    bets = self.bets.played(false).all :include=>{:match=>[:home_team,:away_team]},:order=>"#{Match.table_name}.begin_at ASC"
    bets.size
  end

  # Previous lost bet
  def last_lose_bet
    bets.first :conditions=>{:played=>true,:win=>false},
      :order=>"#{Match.table_name}.begin_at DESC",
      :include=>:match
  end

  # All actions will be sent
  # Only send latest feed
  def publish_delayed_actions
    WinstreakPublisher # auto load this class, let user_action.feed find this class
    user_action = user_actions.first :order=>"created_at DESC"
    if user_action
      user_action.feed.send_delayed_message
      # destroy all
      user_actions.destroy_all
    end
  end

  alias_method :publish_delayed_action,:publish_delayed_actions


  # Calculate total score based equation below:
  # Total score = 10000 * max(0.1,Current Streak/15) * max(0.1,Total Bets/500) * max(0.1,Best Streak/15) * Current Bet(0.1,1)
  def calc_score
    self.score = 1000.0 *
       [0.1,current_winstreak_count/15].max *
       [0.1,bets_count/500].max *
       [0.1,best_winstreak.try(:streak_count).to_i].max *
       [0.1,current_streak_bets_count].max
    self.score = self.score.round
    save false
  end

end
